/************************************************************************/
/* Name : Kiwon Yun
/* Partner's name : Eric Rooney
/* File Name : Dealer.cpp
/* The name of assignment : Iteration 2
/************************************************************************/

#include "Dealer.h"

Dealer::Dealer(void)
{
	m_pDeck = NULL;
	m_pGameInfo = GameInfo::GetObjPtr();
	m_pGameInfo->Init( this );

	m_pRuleManager = new RuleManager;

	m_pCurRoundInfo = NULL;
	m_pBetting = NULL;
	m_nBetMoney=10;
}

Dealer::~Dealer(void)
{
	ClearGame();

	GameInfo::GetObjPtr()->DestroySingleton();

	if( m_pRuleManager )
	{
		delete m_pRuleManager;
		m_pRuleManager = NULL;
	}

	if( m_pCurRoundInfo )
	{
		delete m_pCurRoundInfo;
		m_pCurRoundInfo = NULL;
	}

	if( m_pBetting )
	{
		delete m_pBetting;
		m_pBetting = NULL;
	}
}

void Dealer::ClearGame()
{
	if( m_pDeck )
	{
		delete m_pDeck;
		m_pDeck = NULL;
	}

	// Just clear vector containing hands, 
	// because pointers of cards on hands were cleared already when the deck was deleted.
	m_vecHand.clear();

	// clean all player
	vector <Player*>::iterator it;
	for ( it = m_vecPlayer.begin(); it != m_vecPlayer.end( ) ; ++it )
	{
		Player* pPlayer = (*it);

		if( pPlayer != NULL )
		{
			delete pPlayer;
			pPlayer = NULL;
		}
	}
	m_vecPlayer.clear();

	GameInfo::GetObjPtr()->SetStartGame( false );
	GameInfo::GetObjPtr()->SetEndGame( false );
}

void Dealer::CreateGame( E_MODE a_mode, char* a_strTestFileName )
{
	m_exeMode = a_mode;
	m_strTestFileName = a_strTestFileName;

	CreateDeckAndDeal();

	GameInfo::GetObjPtr()->SetStartGame( true );
	m_pBetting = new Betting();
	m_pCurRoundInfo = new RoundInfo();

#ifdef CAN_SET_MONEY_TEST_MODE
	int arrMoney[6] = { 10, 50, 50, 50, 50, 50 };

	int i = 0;
	vector <Player*>::iterator it;
	for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); ++it )
	{
		Player* pPlayer = (*it);
		pPlayer->SetMoney( arrMoney[i] );
		i++;
	}

#else
	vector <Player*>::iterator it;
	for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); ++it )
	{
		Player* pPlayer = (*it);
		pPlayer->SetMoney( m_nBetMoney );
	}
#endif			
}

void Dealer::CreateDeckAndDeal()
{
	if( m_pDeck )
	{
		delete m_pDeck;
		m_pDeck = NULL;
	}

	// all players clear hand
	vector <Player*>::iterator it;
	for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); ++it )
	{
		Player* pPlayer = (*it);
		Hand* pHand = pPlayer->GetHand();
		pHand->ClearHand();
	}

	// Create a deck
	m_pDeck = new Deck;

	// Initiate a deck
	m_pDeck->InitDeck( m_exeMode, m_strTestFileName );

	itCurPlayer = m_vecPlayer.begin();

#ifdef SCORE_TEST_MODE
	// for test
	vector <Player*>::iterator it2;
	for( it2 = m_vecPlayer.begin(); it2 != m_vecPlayer.end(); ++it2 )
	{
		Player* pPlayer = (*it2);
		for( int i = 0; i < 5; i++ )
		{
			pPlayer->RequestCard();
		}
	}
#else
	// Deal five cards for each player
	for( int i = 0; i < 5; i++ )
	{
		vector <Player*>::iterator it;
		for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); ++it )
		{
			Player* pPlayer = (*it);
			pPlayer->RequestCard();
		}
	}
#endif
}

void Dealer::AddPlayer( const char* a_strPlayerName )
{
	Player* pPlayer = new Player( this );
	pPlayer->SetPlayerName( a_strPlayerName );

	m_vecPlayer.push_back( pPlayer );
	m_vecHand.push_back( pPlayer->GetHand() );
}

int Dealer::GetRemaingCardNum()
{
	return m_pDeck->getSize();
}

void Dealer::ShowHands()
{
	vector <Player*>::iterator it;
	for ( it = m_vecPlayer.begin(); it != m_vecPlayer.end( ) ; ++it )
	{
		Player* pPlayer;
		pPlayer = (*it);
		cout << "Player's name : " << pPlayer->GetPlayerName() << endl;
		pPlayer->ShowHand();
	}
}

void Dealer::ShowCurHand()
{
	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
	{
		cout << "Player's name : " << pPlayer->GetPlayerName() << endl;
		pPlayer->ShowHand();
	}
}

void Dealer::Discard(int m_nCard1,int m_nCard2,int m_nCard3)
{	
	int m_nNumberDeleted=0;

	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
	{
		m_nNumberDeleted=pPlayer->Discard(m_nCard1,m_nCard2,m_nCard3);

		for(int i=0; i<m_nNumberDeleted; i++)
		{
			pPlayer->RequestCard();
		}
		
		pPlayer->ReorderCards(m_nCard1,m_nCard2,m_nCard3);
		
	}
}

int Dealer::GetPlayersNum()
{
	return (int) m_vecPlayer.size();
}

int Dealer::GetHandsNum()
{
	return (int) m_vecHand.size();
}

char* Dealer::GetCurPlayerName()
{
	char* strPlayerName = NULL;

	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
	{
		strPlayerName = pPlayer->GetPlayerName();
	}

	return strPlayerName;
}

bool Dealer::GotoNextPlayer()
{
	itCurPlayer++;  
	if(itCurPlayer == m_vecPlayer.end())
		return false;
	else
		return true;
}

void Dealer::RequestCard( Player* a_pPlayer )
{
	Card* pCard = m_pDeck->RequestCard();

	if( pCard != NULL )
	{
		Hand* pHand = a_pPlayer->GetHand();
		if( pHand )
			pHand->AddCard( pCard );
	}
	else
	{
		cout << "Failed to draw cards" << endl;
	}
}

void Dealer::CheckMoneyOfPlayers()
{
	vector<Player*>::iterator it;
	int nNoMoneyPlayer = 0;
	for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); it++ )
	{
		Player* pPlayer = (*it);
		if( pPlayer->GetMoney() == 0 )
			nNoMoneyPlayer++;
	}

	// if the number of players that have some money is less than 1, the game is ended. 
	if( m_vecPlayer.size() - nNoMoneyPlayer <= 1 )
	{
		GameInfo::GetObjPtr()->SetEndGame( true );
	}
}

void Dealer::ProcessNextTurn()
{
	bool bExistOnlyOne = false;

	//Test for only 1 player remaining and end the game if true
	if((GetPlayersNum()- m_pBetting->GetFolds()- m_pBetting->GetAllIns())<2)
	{	
		bExistOnlyOne = true;
		m_pCurRoundInfo->AddSubRound();
	}
		
	// begin new round (the first bet round)
	if( m_pCurRoundInfo->GetSubRound() == 0 )
	{
		if(GotoNextPlayer() == false)
			itCurPlayer = m_vecPlayer.begin();

		bool bEndRound = false;
		if( m_pBetting->GetLastBet() == 0 )
		{
			if( m_pBetting->GetCalls() == GetPlayersNum() - m_pBetting->GetFolds()- m_pBetting->GetAllIns() )
				bEndRound = true;
		}
		else
		{
			if( m_pBetting->GetCalls() == GetPlayersNum() - 1 -m_pBetting->GetFolds()- m_pBetting->GetAllIns() )
				bEndRound = true;
		}

		if( bEndRound )
		{
			m_pCurRoundInfo->AddSubRound();
			itCurPlayer = m_vecPlayer.begin();
			m_pBetting->ResetAllData();

			vector<Player*>::iterator itPlayer;
			for( itPlayer = m_vecPlayer.begin(); itPlayer != m_vecPlayer.end(); itPlayer++ )
			{
				Player* pPlayer = (*itPlayer);
				pPlayer->SetBetSoFar(0);
			}
		}
		Player* pPlayer = (*itCurPlayer);
		if(pPlayer->GetFold() == true || pPlayer->GetAllIn()==true )
		{
				ProcessNextTurn();
		}
	}

	// the sub-round that can bet
	else if( IsCanBet() && 
		( ( m_pBetting->GetLastBet() == 0 && m_pBetting->GetCalls() < GetPlayersNum() - m_pBetting->GetFolds() - m_pBetting->GetAllIns()) || 
		( m_pBetting->GetLastBet() != 0 && m_pBetting->GetCalls() < GetPlayersNum() - 1 - m_pBetting->GetFolds() - m_pBetting->GetAllIns()) ) )
	{
		if(GotoNextPlayer() == false)
			itCurPlayer = m_vecPlayer.begin();
		Player* pPlayer = (*itCurPlayer);
		if( pPlayer )
		{
			if(pPlayer->GetFold() == true || pPlayer->GetAllIn()==true)
			{
				ProcessNextTurn();
			}
		}
	}

	// pass-discard round or end round
	else
	{
		// A round is ended, so select winner, distribute money, recreate deck and so on.
		if( GotoNextPlayer() == false || ( m_pBetting->GetCalls() > GetPlayersNum() - 2 - m_pBetting->GetFolds() - m_pBetting->GetAllIns()))
		{
			if( m_pCurRoundInfo->AddSubRound() == false || bExistOnlyOne )
			{
				// A round is ended
				CalcRankScore();
				DistributeMoney();
				CheckMoneyOfPlayers();

				// if the game is ended
				if( GameInfo::GetObjPtr()->IsEndGame() == true )
				{
					Player*	pWinner = m_pCurRoundInfo->GetCurWinner();
					cout << "***********************************************" << endl;
					cout << "* THE NAME OF AN OVERALL WINNER : " << pWinner->GetPlayerName() << endl;
					cout << "***********************************************" << endl;

					cout << "This game ended, if you want to play another game, input n or N" << endl;

					// clear previous game
					ClearGame();
					return;
				}

				if( m_pCurRoundInfo )
				{
					delete m_pCurRoundInfo;
					m_pCurRoundInfo = NULL;
				}

				if(m_pBetting)
				{
					delete m_pBetting;
					m_pBetting = NULL;
				}

				// create new deck and deal cards
				CreateDeckAndDeal();

				vector<Player*>::iterator itPlayer;
				for( itPlayer = m_vecPlayer.begin(); itPlayer != m_vecPlayer.end(); itPlayer++ )
				{
					Player* pPlayer = (*itPlayer);
					pPlayer->SetAllIn(false);
					pPlayer->SetBetSoFar(0);
					pPlayer->SetFold(false);
					pPlayer->SetAllInAmount(0);
				}

				m_pCurRoundInfo = new RoundInfo;
				m_pBetting = new Betting();

				GameInfo::GetObjPtr()->AddRound();

//				m_pBetting->ResetAllData();

				// rearrange the players so that a new player is to the left of the dealer
				rotate( m_vecPlayer.begin(), m_vecPlayer.begin() + 1, m_vecPlayer.end() );

				// ante
				Ante();

			}
			
			itCurPlayer = m_vecPlayer.begin();
			Player* pPlayer = (*itCurPlayer);
			if( pPlayer )
			{
				if(pPlayer->GetFold() == true || pPlayer->GetAllIn()==true)
				{
					ProcessNextTurn();
				}
			}
		}

		// pass-discard round
		else
		{	
			Player* pPlayer = (*itCurPlayer);
			if( pPlayer )
			{
				if(pPlayer->GetFold() == true || (pPlayer->GetAllIn()==true && m_pCurRoundInfo->GetSubRound()!=1))
				{
					ProcessNextTurn();
				}
			}

		}
	}
}

void Dealer::CalcRankScore()
{
	vector <Player*>::iterator it;
	for ( it = m_vecPlayer.begin(); it != m_vecPlayer.end( ) ; ++it )
	{
		Player* pPlayer = (*it);

		if( !pPlayer->GetFold() )
		{
			cout << "=====================" << endl;
			cout << "Player (" << pPlayer->GetPlayerName() << ")" << endl;
			pPlayer->ShowHand();

			// select winner
			Player*	pCurWinner;
			pCurWinner = m_pRuleManager->SelectWinner( pPlayer, m_pCurRoundInfo->GetCurWinner() );

			// If the new winner occurs
			if( pCurWinner != m_pCurRoundInfo->GetCurWinner() )
			{
				// If two players are tied
				if( !pCurWinner )
				{
					m_pCurRoundInfo->ClearTiedWinnerPlayer();
					m_pCurRoundInfo->AddTiedWinnerPlayer( pPlayer );
					m_pCurRoundInfo->AddTiedWinnerPlayer( m_pCurRoundInfo->GetCurWinner() );
					m_pCurRoundInfo->SetCurWinner( pPlayer );
				}
				else
				{
					// select second winner (set by current winner)
					m_pCurRoundInfo->SetCurSecondWinner( m_pCurRoundInfo->GetCurWinner() );					

					m_pCurRoundInfo->ClearTiedWinnerPlayer();
					m_pCurRoundInfo->SetCurWinner( pCurWinner );
				}
			}

			else
			{
				// select second winner (set by winner between current second winner and this player)
				Player*	pSecondCurWinner = m_pRuleManager->SelectWinner( pPlayer, m_pCurRoundInfo->GetCurSecondWinner() );
				m_pCurRoundInfo->SetCurSecondWinner( pSecondCurWinner );
			}

/*			
			if( pCurWinner )
			{
				cout << "The winner : " << pCurWinner->GetPlayerName() << endl;
			}
			else
			{
				cout << "The comparison is tied" << endl;
				for( int i = 0; i < m_pCurRoundInfo->GetTiedWinnerNumber(); i++ )
				{
					Player* pWinner = m_pCurRoundInfo->GetTiedWinner( i );
					cout << "The winner : " << pWinner->GetPlayerName() << endl;
				}
			}
*/
		}
	}

	Player* pWinner = NULL;
	if( m_pCurRoundInfo->GetTiedWinnerNumber() > 0 )
	{
		cout << "This round is tied" << endl;

		for( int i = 0; i < m_pCurRoundInfo->GetTiedWinnerNumber(); i++ )
		{
			Player* pWinner = m_pCurRoundInfo->GetTiedWinner( i );
			cout << "The winner of this round :  : " << pWinner->GetPlayerName() << endl;
		}
	}
	else
	{
		pWinner = m_pCurRoundInfo->GetCurWinner();
		assert(pWinner);
		cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" << endl;
		cout << "@ The winner of this round : " << pWinner->GetPlayerName() << endl;
		cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" << endl;
		
//		Player* pSecondWinner = m_pCurRoundInfo->GetCurSecondWinner();
//		if( pSecondWinner )
//			cout << "Second Winner : " << pSecondWinner->GetPlayerName() << endl;
	}
}

void Dealer::Ante()
{
	cout<<"Each player has now anted $1"<<endl;
	vector <Player*>::iterator it;
	for( it = m_vecPlayer.begin(); it != m_vecPlayer.end(); ++it )
	{
		Player* pPlayer = (*it);
		m_pBetting->Ante(pPlayer);
	}
}

void Dealer::ShowTestLog()
{
	cout << "Is the game started? : " << GameInfo::GetObjPtr()->IsStartGame() << endl;
	cout << "Is the game ended? : " << GameInfo::GetObjPtr()->IsEndGame() << endl;
	cout << "Current Round : " << GameInfo::GetObjPtr()->GetRound() + 1 << endl;
	cout << "Current Bet Round : " << m_pCurRoundInfo->GetSubRound() + 1 << endl;

	cout << "Who is winner in this round? : ";
	Player* pPlayer = m_pCurRoundInfo->GetCurWinner();
	if( pPlayer != NULL )
		cout << pPlayer->GetPlayerName() << endl;
	else 
		cout << "There is no winner yet." << endl;
}

void Dealer::Call()
{
	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
		m_pBetting->Call(pPlayer, Dealer::GetPlayersNum()-m_pBetting->GetFolds());  
}

void Dealer::ShowMoney()
{
    Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
	    cout<<"You have $"<<pPlayer->GetMoney()<<endl;  
}

void Dealer::Raise(int * m_nAmount)
{
	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
		m_pBetting->Raise(pPlayer,m_nAmount);
}

void Dealer::Fold()
{
	Player* pPlayer = (*itCurPlayer);
	if( pPlayer )
		m_pBetting->Fold(pPlayer);
}

int Dealer::GetSubRoundNum()
{
	if( m_pCurRoundInfo )
		return m_pCurRoundInfo->GetSubRound();
	else
		return -1;
}

int Dealer::GetRaises()
{
	return m_pBetting->GetRaises();
}

void Dealer::DistributeMoney()
{
	Player* pWinner = NULL;
	Player* pSecondWinner = NULL;
	int nTiedWinnerNum = m_pCurRoundInfo->GetTiedWinnerNumber(); 

	if( nTiedWinnerNum > 0 )
	{
		for( int i = 0; i < nTiedWinnerNum; i++ )
		{
			pWinner = m_pCurRoundInfo->GetTiedWinner( i );
			m_pBetting->TieDistributeMoney(pWinner, nTiedWinnerNum);
		}
	}
	else
	{
		pWinner = m_pCurRoundInfo->GetCurWinner();
		pSecondWinner = m_pCurRoundInfo->GetCurSecondWinner();
		m_pBetting->DistributeMoney( pWinner, pSecondWinner );
	}
}

bool Dealer::IsCanBet()
{
	return m_pCurRoundInfo->IsCanBet();
}

void Dealer::SetBetMoney(int a_nBetMoney)
{
	m_nBetMoney=a_nBetMoney;
}
